package com.validateCodeYzm.zpf.lib;/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */


import java.util.*;
import java.util.function.*;

public final class Result<T> {

    // main param
    private final Exception e;

    private final T value;

    private final String error;

    private Integer code = 0;

    // constructor
    private Result(T value,String error,Exception e) {
        this.e = e;
        this.value = value;
        this.error = error;
    }

    public Result<T> ifError(String changeError) {
        if(ifNotPresent()){
            return Result.error(changeError);
        }
        return this;
    }

    public Result<Integer> castInt() {
        return flatMap(value->{
            try{
                return Result.of(Integer.valueOf(value.toString()));
            }catch(Exception e){
                e.printStackTrace();
            }
            return Result.error("cast Integer error:["+value+"]");
        });
    }

    @FunctionalInterface
    public interface DoubleFunction<A,B,C>{
        C apply(A f,B u);
    }

    @FunctionalInterface
    public interface TripleFunction<A,B,C,D>{
        D apply(A f,B u,C c);
    }

    @FunctionalInterface
    public interface FourFunction<A,B,C,D,E>{
        E apply(A f,B u,C c,D d);
    }

    @FunctionalInterface
    public interface FiveFunction<A,B,C,D,E,F>{
        F apply(A f,B u,C c,D d,E e);
    }

    private static Result<?> checkError(Result<?> ...res){
        for(Result<?> r:res){
            if(r.ifNotPresent())
                return Result.error(r);
        }
        return Result.of("pass");
    }

    public static <A,B,C>  Result<C> of(Result<A> a,Result<B> b,DoubleFunction<A,B,C> f){
        Result<?> result = checkError(a, b);
        if(result.ifNotPresent())
            return Result.error(result);
        return Result.of(f.apply(a.getValue(),b.getValue()));
    }

    public static <A,B,C>  Result<C> ofFlat(Result<A> a,Result<B> b,DoubleFunction<A,B,Result<C>> f){
        Result<?> result = checkError(a, b);
        if(result.ifNotPresent())
            return Result.error(result);
        return f.apply(a.getValue(),b.getValue());
    }

    public static <A,B,C,D>  Result<D> ofFlat(Result<A> a,Result<B> b,Result<C> c,TripleFunction<A,B,C,Result<D>> f){
        Result<?> result = checkError(a, b,c);
        if(result.ifNotPresent())
            return Result.error(result);
        return f.apply(a.getValue(),b.getValue(),c.getValue());
    }


    public static <A,B,C,D>  Result<D> of(Result<A> a,Result<B> b,Result<C> c,TripleFunction<A,B,C,D> f){
        Result<?> result = checkError(a,b,c);
        if(result.ifNotPresent())
            return Result.error(result);
        return Result.of(f.apply( a.getValue(),b.getValue(),c.getValue() ));
    }

    public static <A,B,C,D,E>  Result<E> of(Result<A> a,Result<B> b,Result<C> c,Result<D> d,FourFunction<A,B,C,D,E> f){
        Result<?> result = checkError(a,b,c,d);
        if(result.ifNotPresent())
            return Result.error(result);
        return Result.of(f.apply( a.getValue(),b.getValue(),c.getValue(),d.getValue()));
    }

    public static <A,B,C,D,E,F>  Result<F> of(Result<A> a,Result<B> b,Result<C> c,Result<D> d,Result<E> e,FiveFunction<A,B,C,D,E,F> f){
        Result<?> result = checkError(a,b,c,d,e);
        if(result.ifNotPresent())
            return Result.error(result);
        return Result.of(f.apply( a.getValue(),b.getValue(),c.getValue(),d.getValue(),e.getValue()));
    }

    public static <T> Result<T> of(T value) {
        if(Objects.nonNull(value)){
            return new Result<T>(value,null,null);
        }else{
            return Result.error("value is null",null);
        }
    }

    public static <T> Result<T> of(Supplier<T> supplier) {
        T value = supplier.get();
        return of(value);
    }

    public static <T> Result<T> ofSupply(Supplier<T> supplier) {
        return of(supplier.get());
    }


    public static <T> Result<T> ofOptional(Optional<T> option) {
        return option.map( x -> new Result<T>(option.get(),null,null) ).orElseGet( ()-> Result.error("is null",null) );
    }

    public static <T> Result<T> error(String error) {
        return new Result<T>(null,error,null);
    }

    public static <T> Result<T> errorCode(Integer code,String error) {
        Result<T> tResult = new Result<>(null, error, null);
        tResult.setCode(code);
        return tResult;
    }

    public static <T> Result<T> error(Result<?> errorResult) {
        return new Result<T>(null,errorResult.getError(),errorResult.toGetException());
    }

    public static <T> Result<T> error(String error,Exception e) {
        String add = Objects.nonNull(e) ? ":"+e.getMessage() : "";
        return new Result<T>(null,error+add,e);
    }

    public static <T> Result<T> error(Exception e) {
        return new Result<T>(null,"occur error:"+e.getMessage(),e);
    }

    // getter method
    public T getValue() {
        return value;
    }

    public T safeGet(){
        ifNotPresent(err -> {
            throw new NoSuchElementException("value not exist,please to do check the value if present before get");
        });
        return value;
    }

    public String getError() {
        return error == null ? "":error;
    }

    public Exception toGetException(){
        return e;
    }

    public boolean ifNotPresent() {
        return !ifPresent();
    }

    public Result<T> ifNotPresent(Consumer<String> consumer) {
        if(ifNotPresent()){
            consumer.accept(error);
        }
        return this;
    }

    public Result<T> mapError(Function<String,String> fn){
        if(ifNotPresent()){
            return Result.error(fn.apply(error));
        }
        return this;
    }

    public Result<T> consumerError(Consumer<String> fn){
        if(ifNotPresent()){
            fn.accept(error);
        }
        return this;
    }

    public Result<T> consumerErrorAndException(BiConsumer<String,Exception> fn){
        if(ifNotPresent()){
            fn.accept(error,e);
        }
        return this;
    }


    public boolean ifPresent() {
        return value != null;
    }

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    public void ifPresent(Consumer<? super T> consumer,Consumer<String> errConsumer) {
        if (value != null) {
            consumer.accept(value);
        }else {
            errConsumer.accept(error);
        }
    }

    public Result<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!ifPresent())
            return this;
        else
            return predicate.test(value) ? this : new Result<T>(null,"test not pass:"+value,null);
    }


    public<U> Result<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if(ifPresent()){
            U u = mapper.apply(value);
            if(Objects.isNull(u)){
                return  Result.error("value is null!");
            }
            if (u.getClass() == Result.class) {
                throw new NoSuchElementException("occur nested ,the result is still Result<?> class ! please  use flatMap method to instead of map method ");
            }
            return new Result<U>(u,null,null);
        }else {
            return new Result<U>(null,error,e);
        }
    }

    public Result<T> consumer(Consumer<T> consumer){
        if(ifPresent()){
            consumer.accept(value);
        }
        return this;
    }
    public Result<T> consumer(Consumer<T> consumer,Consumer<String> errConsumer){
        if(ifPresent()){
            consumer.accept(value);
        }else{
            errConsumer.accept(error);
        }
        return this;
    }

    public Result<T> print(){
        consumer(System.out::println,System.out::println);
        return this;
    }


    public<U> Result<U> flatMap(Function<? super T, Result<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!ifPresent())
            return new Result<U>(null,error,e);
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    @SuppressWarnings("unchecked")
    public <U> Result<U> convertType(Class<U> clazz){
        return flatMap(value -> {

            if(clazz.isAssignableFrom(value.getClass())){
                return Result.of((U)value);
            }else {
                return Result.error("class not match");
            }

        });
    }


    public T toGetOrElseValue(T other) {
        return value != null ? value : other;
    }

    public T toGetOrElse(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    public  T toOrGetError(Function<String,T> other) {
        return value != null ? value : other.apply(error);
    }

    public  T toGetOrConsumerError(Consumer<String> consumer) {
        if(ifNotPresent())
            consumer.accept(error);
        return value;
    }

    public <X extends Throwable> T toGetOrElseThrow(Function<String,? extends X>   exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.apply(error);
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Result)) {
            return false;
        }

        Result<?> other = (Result<?>) obj;
        return Objects.equals(value, other.value);
    }


    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }


    @Override
    public String toString() {
        return value != null
                ? String.format("Result[%s]", value)
                : "Result.empty";
    }

    public Integer getCode() {
        return code;
    }

    public Result<T> setCode(Integer code) {
        this.code = code;
        return this;
    }

    public static <T> Result<T> catchException(Supplier<T> supplier){
        try{
            return Result.of(supplier.get());
        }catch(Exception e){
            return Result.error("错误:"+e.getMessage());
        }
    }


}
